从上一篇的案例中我们可以看到 Spring AOP 已经初露锋芒了。接下来我们将详细介绍 Spring AOP 的详细使用。Spring AOP 使用方式有两种:XML 配置 和 注解方式。本篇笔记都是基于注解形式的。
名称介绍
切面(Aspect):通知 + 切点 = 切面
切点(Pointcut):如果通知定义了“什么”和“何时”。那么切点就定义了“何处”。切点会匹配通知所要织入的一个或者多个连接点。
通知(Advice):通知定义了切面是什么以及何时使用。除了秒速切面要完成的工作,通知还解决了何时执行这个工作的问题。通知分为 前置通知、后置通知、返回通知、异常通知、环绕通知。
连接点(JoinPoint):连接点是一个应用执行过程中能够插入一个切面的点。就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。其他如AspectJ还可以让你在构造器或属性注入时都行,不过那不是咱们关注的,只要记住,和方法有关的前前后后都是连接点。
引入(Introduction):引入允许我们向现有的类添加新方法或属性(以后有空再研究)
目标(Target):引入中所提到的目标类,也就是要被通知的对象
织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有3个点可以进行织入
编译期——切面在目标类编译时期被织入,这种方式需要特殊的编译器。AspectJ 的织入编译器就是以这种方式织入切面。
类加载期——切面在目标类加载到JVM时被织入 ,这种方式需要特殊的类加载器,他可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5 的 加载时织入(load-time weaving,LTW) 就支持这种织入方式
运行期——切面在应用运行期间的某个时刻被织入。一般情况下,在织入切面的时候,AOP 容器会为目标对象动态的创建代理对象。Spring AOP 就是以这种方式织入切面。
AspectJ 注解
切面类
- @Aspect:标注该类为切面类
切点
- @Ponitcut:标注的方法为切点
通知
@Before:在目标方法被调用之前调用通知功能
@After:在目标方法完成之后调用通知,无论方法执行成功与否
@AfterReturning:在目标方法成功执行之后调用
@AfterThrowing:在目标方法抛出异常时调用通知
@Around:环绕通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
Pointcut expression
切点表达式分为三部分:designators、wildcards、operators。
designators(指示器)

1. 匹配方法
execution()(重点掌握)@Pointcut("execution(public * com.fatal.service.*Service.*(..))")匹配任意返回值,service包下的以Service结尾的所有方法
@Pointcut("execution(public * com.fatal.service..*Service.*(..))")匹配任意返回值,service包及其子包下的以Service结尾的所有方法
@Pointcut("execution(public String com.fatal.service..*Service.*(..))")匹配返回String类型,service包及其子包下的以Service结尾的所有方法
@Pointcut("execution(public * com.fatal.service..*Service.*(..) throws *)")匹配任意返回值,service包及其子包下的以Service结尾,可能抛出任意异常的所有方法(只有方法上面标注了 throws … 才会被匹配到)

2. 匹配注解
@annotation()(关注点为方法级别)@Pointcut(“@annotation(com.fatal.anno.AdminOnly)”)
匹配所有标注有
@AdminOnly注解的方法
@within()(关注点为类级别)@Pointcut(“@within(com.fatal.anno.NeedSecured)”)
匹配所有标注有
@NeedSecured的类的所有方法,要求 annotation 的 RetentionPolity 为 CLASS
@target()(关注点为类级别)与 within() 一起用@Pointcut(“@target(com.fatal.anno.NeedSecured)”)
匹配所有标注有
@NeedSecured的类的所有方法,要求: annotation 的 RetentionPolity 为 RUNTIME ;必须与 within() 一起用
@args()(关注点为参数级别)与 within() 一起用@Pointcut(“@args(com.fatal.anno.NeedSecured)”)
匹配传入的参数类标注有
@NeedSecured的方法;要求必须与 within() 一起用

@within() 和 @target() 的区别
在 Spring 环境下的话,也就是我们
演示的环境,它们没有任何区别;当不在 Spring 环境下的时候,它们的区别如下:
- 要求 annotation 的 RetentPolicy 为 class 级别
- 要求 annotation 的 RetentPolicy 为 runtime 级别
补充
这里补充一下,使用 @target() 和 @args() 必须指定需要扫描的类的范围,不然会报错,如视频中,去掉 within 限制 @args() 、 @target() 都会报错。
3. 匹配包/类
within()@Pointcut(“within(com.fatal.service.ProductService)”)
匹配 指定类 的所有方法
@Pointcut(“within(com.fatal.service..*)”)
匹配 指定包及子包下所有类 的所有方法

4. 匹配对象
this()@Pointcut(“this(com.fatal.log.Loggable)”)
匹配 指定接口 生成的组件的
代理对象的方法
target()@Pointcut(“target(com.fatal.log.Loggable)”)
匹配 指定接口 生成的组件的
被代理对象的方法
bean()@Pointcut(“bean(beanId)”)
匹配组件名为
beanId的bean里边的方法注意:括号里边为bean的
id。默认为类名首字母小写@Pointcut(“bean(*Service)”)
匹配所有 以Service结尾 的bean里头的方法

this() 与 target() 的区别
这里和
引入有关了
- this: 可以拦截 DeclareParents(Introduction)
- target: 不拦截 DeclareParents(Introduction)
5. 匹配参数
args()@Pointcut(“args(Long) && within(com.fatal.service.*)”)
匹配 指定类 中任何 以find开头而且只有一个Long参数 的方法
@Pointcut(“args(Long,..) && within(com.fatal.service.*)”)
匹配 指定类 中任何 以find开头的而且第一个参数为Long型 的方法

wildcards(通配符)
- *:匹配任意数量的字符
- +:匹配指定类及其子类
- ..:一般用于匹配任意数的子包或参数
operators(运算符)
- && (与)
- ||(或)
- !(非)
Advice
@Before:前置通知
@After:后置通知,方法执行之后(相当于
finally)
测试结果:相当于
finally。方法执行之后不管是否(抛异常方法就不能返回)正常返回都会走切面逻辑@AfterReturning:返回通知,成功执行之后

测试结果:不管是否有返回值,只要方法正常执行完成就会走
切面逻辑@AfterThrowing:异常通知,抛出异常之后(只要抛异常,就会走切面逻辑)

测试结果:只要方法一抛出异常,就会走
切面逻辑@Around:环绕通知(相当于其他注解的总和)

测试结果:功能相当于前置通知、后置通知、返回通知、环绕通知的结合
笔记
- 返回通知怎么获取
目标方法返回值
1 | /** |
- execution 只能修饰公共方法,public 可以省略
参考资料
总结
本篇笔记记录的内容:
- Pointcut expression 的组成部分
- 各种 designators 的区别
- 5种 advice 及参数/结果绑定
SpringBoot 自动开启 AOP 功能了,我们可以直接使用 @Transactional 、@Async 或者自定义的基于AOP 的注解实现切面编程
源码有点多,就不贴出来了。需要的话去 GitHub 上看。可以检出来跑跑看。。。
SpringBoot的知识已经有前辈在我们之前探索了。比较喜欢的博主有:唐亚峰 | Battcn、方志朋的专栏、程序猿DD、纯洁的微笑。对这门技术感兴趣的可以去他们的博客逛逛。谢谢他们的分享~~
以上文章是我用来学习的Demo,都是基于 SpringBoot2.x 版本。
源码地址: https://github.com/ynfatal/springboot2-learning/tree/master/chapter25_2